package com.itheima.appserver.service.impl;
import java.util.Date;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.appserver.exception.MyException;
import com.itheima.appserver.exception.ResultError;
import com.itheima.appserver.interceptor.UserThreadLocal;
import com.itheima.appserver.service.PublishService;
import com.itheima.commons.constants.Constants;
import com.itheima.commons.pojo.*;
import com.itheima.commons.utils.RelativeDateFormat;
import com.itheima.commons.utils.UploadPicUtil;
import com.itheima.commons.vo.PageResult;
import com.itheima.commons.vo.QuanZiVo;
import com.itheima.commons.vo.VisitorsVo;
import com.itheima.interfaces.*;
import lombok.SneakyThrows;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

@Service
public class PublishServiceImpl implements PublishService {

    @Reference
    private AlbumApi albumAPI;

    @Reference
    private PublishApi publishAPI;

    @Reference
    private TimeLineApi timeLineAPI;

    @Reference
    private UserInfoApi userInfoAPI;

    @Autowired
    private PidService pidService;

    @Reference
    private UsersApi usersApi;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Reference
    private CommentApi commentAPI;

    @Reference
    private AuditStateApi auditStateApi;

    @SneakyThrows
    @Override
    public void publishMessage(String textContent, String location, String longitude, String latitude, MultipartFile[] multipartFile) {
        if (StrUtil.isEmpty(textContent)) {
            throw new MyException(ResultError.textContentIsNull());
        }
        List<String> list = new ArrayList<>();
        if (ArrayUtil.isNotEmpty(multipartFile)) {
            for (MultipartFile file : multipartFile) {
                String uploadPic = UploadPicUtil.uploadPic(StrUtil.subAfter(file.getOriginalFilename(), ".", true), file.getBytes());
                list.add(uploadPic);
            }
        }

        Publish publish = new Publish();
        publish.setId(new ObjectId());
        publish.setPid(pidService.pidPro("PUBLISH"));
        publish.setUserId(UserThreadLocal.getUserId());
        publish.setText(textContent);
        publish.setMedias(list);
        publish.setSeeList(Collections.emptyList());
        publish.setNotSeeList(Collections.emptyList());
        publish.setLongitude(longitude);
        publish.setLatitude(latitude);
        publish.setLocationName(location);
        publish.setCreated(System.currentTimeMillis());

        ObjectId objectId = publishAPI.publishMessage(publish);
        Album album = new Album();
        album.setId(new ObjectId());
        album.setPublishId(objectId);
        album.setCreated(System.currentTimeMillis());
        albumAPI.savePublishId(album, UserThreadLocal.getUserId());

        List<Long> longs = timeLineAPI.selectTimeLine(UserThreadLocal.getUserId());

        TimeLine timeLine = new TimeLine();
        timeLine.setId(new ObjectId());
        timeLine.setPublishId(objectId);
        timeLine.setDate(System.currentTimeMillis());
        timeLine.setUserId(UserThreadLocal.getUserId());
        //加入到状态表中
        AuditState auditState = new AuditState();
        auditState.setUserId(publish.getUserId());
        auditState.setTopState(1);
        auditState.setState("1");
        auditState.setPublishId(publish.getId().toHexString());
        auditState.setCreated(new Date());
        auditState.setUpdated(new Date());


        auditStateApi.saveState(auditState);
        timeLineAPI.savePublishMessage(timeLine, longs);
    }

    @Override
    public PageResult findFriendMovements(Integer page, Integer pagesize) {
        //根据自己的时间线表 查询好友发送发布表的信息
        List<Publish> publishes = publishAPI.selectPublishId(page, pagesize, UserThreadLocal.getUserId());

        if (CollUtil.isEmpty(publishes)) {
            return new PageResult();
        }
        //从发布表中获得好友id 查询好友信息
        List<Long> userId = CollUtil.getFieldValues(publishes, "userId", Long.class);
        // 获得好友信息
        Map<Long, UserInfo> userInfoByUserId = userInfoAPI.findPublishInfo(userId);

        // 对两张表数据拼凑
        List<QuanZiVo> quanZi = createQuanZi(publishes, userInfoByUserId);

        PageResult pageResult = new PageResult(page, pagesize, 0L, quanZi);


        return pageResult;
    }

    @Override
    public PageResult findRecommendFriend(Integer page, Integer pagesize) {
        //从redis中查询自己所拥有的pid
        String pids = redisTemplate.opsForValue().get(Constants.QUANZI_PUBLISH_RECOMMEND + UserThreadLocal.getUserId());
        //用pid去发布表中查询发布信息
        List<String> stringList = StrUtil.split(pids, ",");
        List<Publish> publishes = Collections.emptyList();
        if (StrUtil.isEmpty(pids)) {
            //没有pid 帮忙随机生成
            publishes = publishAPI.randomUserPid(pagesize);
        } else {
            int startIndex = (page - 1) * pagesize;
            List<String> publishIdList = CollUtil.sub(stringList, startIndex, Math.min(startIndex + pagesize, stringList.size()));
            publishes = publishAPI.selectPublishByPid(Convert.toList(Long.class, publishIdList));

        }
        //根据发布表的数据 获得用户id
        List<Long> userId = CollUtil.getFieldValues(publishes, "userId", Long.class);
        //查询数据库查询用户信息
        Map<Long, UserInfo> publishInfo = userInfoAPI.findPublishInfo(userId);

        List<QuanZiVo> quanZi = createQuanZi(publishes, publishInfo);
        return new PageResult(page, pagesize, Convert.toLong(publishes.size()), quanZi);

    }

    @Override
    public Long likeComment(String id) {
        Comment comment = new Comment();
        comment.setId(new ObjectId());
        comment.setPublishId(new ObjectId(id));
        comment.setCommentType(1);
        comment.setContent(null);
        comment.setUserId(UserThreadLocal.getUserId());
        Comment publish = commentAPI.selectPublishUserId(new ObjectId(id));
        //说明是评论表id
        if (ObjectUtil.isNotEmpty(publish)) {
            comment.setPublishUserId(publish.getUserId());
        } else {
            //可能是评论表和小视频的点赞  去查询id
            Comment isLike = commentAPI.selectById(id);
            comment.setPublishUserId(isLike.getUserId());

        }
        comment.setIsParent(false);
        comment.setParentId(null);
        comment.setCreated(System.currentTimeMillis());

        Long likeCount = publishAPI.likeComment(comment);
        // 把当前这个圈子的点赞数给保存到redis中
        redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_LIKE_HASHKEY, likeCount.toString());
        // 把自己对这个圈子点赞的标记存储到redis中
        redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_ISLIKE_HASHKEY + UserThreadLocal.getUserId(), "1");
        return likeCount;
    }

    @Override
    public Long noLikeComment(String id) {
        Long comment = publishAPI.NoLikeComment(new ObjectId(id), UserThreadLocal.getUserId());
        redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_LIKE_HASHKEY + UserThreadLocal.getUserId(), comment.toString());
        redisTemplate.opsForHash().delete(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_ISLIKE_HASHKEY + UserThreadLocal.getUserId());
        return comment;
    }

    @Override
    public Long loveComment(String id) {
        Comment comment = new Comment();
        comment.setId(new ObjectId());
        comment.setPublishId(new ObjectId(id));
        comment.setCommentType(3);
        comment.setContent(null);
        comment.setUserId(UserThreadLocal.getUserId());
        Comment publishUserId = commentAPI.selectPublishUserId(new ObjectId(id));
        comment.setPublishUserId(publishUserId.getPublishUserId());
        comment.setIsParent(false);
        comment.setParentId(null);
        comment.setCreated(System.currentTimeMillis());

        Long likeCount = publishAPI.loveComment(comment);
        // 把当前这个圈子的点赞数给保存到redis中
        redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_LOVE_HASHKEY, likeCount.toString());
        // 把自己对这个圈子点赞的标记存储到redis中
        redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_ISLOVE_HASHKEY + UserThreadLocal.getUserId(), "1");
        return likeCount;
    }

    @Override
    public Long noLoveComment(String id) {
        Long comment = publishAPI.NoLoveComment(new ObjectId(id), UserThreadLocal.getUserId());
        redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_LIKE_HASHKEY + UserThreadLocal.getUserId(), comment.toString());
        redisTemplate.opsForHash().delete(Constants.MOVEMENTS_INTERACT_KEY + id, Constants.MOVEMENT_ISLOVE_HASHKEY + UserThreadLocal.getUserId());
        return comment;
    }

    @Override
    public QuanZiVo queryById(String id) {
        // 根据发布表id 查询发布表其他信息
        Publish publish = publishAPI.queryById(new ObjectId(id));
        //查询发布人信息
        UserInfo userInfo = userInfoAPI.findUserInfoByUserId(publish.getUserId());

        List<QuanZiVo> quanZi = createQuanZi(CollUtil.newArrayList(publish), MapUtil.builder(userInfo.getUserId(), userInfo).build());
        return quanZi.get(0);
    }

    @Override
    public PageResult queryAlbumList(Integer page, Integer pagesize, Long userId) {
        //根据用户的id 查询自己的相册表 获得全部动态
        List<Album> albums = albumAPI.selectByUserId(page, pagesize, userId);
        // 根据自己的相册表中的publishId 去发布表中查询数据
        List<String> publish = CollUtil.getFieldValues(albums, "publishId", String.class);
        List<Publish> publishList = publishAPI.selectAlbumById(publish);
        //查询用户信息
        UserInfo userInfo = userInfoAPI.findUserInfoByUserId(userId);

        List<QuanZiVo> ziVoList = createQuanZi(publishList, MapUtil.builder(userInfo.getUserId(), userInfo).build());
        return new PageResult(page, pagesize, 0L, ziVoList);
    }

    @Override
    public List<VisitorsVo> visitors() {

        //查询访问我页面的用户
        List<Visitors> visitorsList = usersApi.visitorsMyHome(UserThreadLocal.getUserId(), Convert.toLong(redisTemplate.opsForValue().get(Constants.VISITORS_USER_KEY + UserThreadLocal.getUserId())));
        if (CollUtil.isEmpty(visitorsList)) {
            return Collections.emptyList();
        }
        List<Long> userId = CollUtil.getFieldValues(visitorsList, "visitorUserId", Long.class);
        Map<Long, UserInfo> userInfoMap = userInfoAPI.findPublishInfo(userId);
        List<VisitorsVo> visitorsVoList = new ArrayList<>();
        for (Visitors visitors : visitorsList) {
            UserInfo userInfo = userInfoMap.get(visitors.getVisitorUserId());
            VisitorsVo visitorsVo = new VisitorsVo();
            visitorsVo.setId(userInfo.getUserId());
            visitorsVo.setAvatar(userInfo.getLogo());
            visitorsVo.setNickname(userInfo.getNickName());
            visitorsVo.setGender(userInfo.getSex() == 1 ? "man" : "woman");
            visitorsVo.setAge(userInfo.getAge());
            visitorsVo.setTags(userInfo.getTags().split(","));
            visitorsVo.setFateValue(ObjectUtil.isNotEmpty(visitors.getScore()) ? Convert.toInt(visitors.getScore()) : 90);
            visitorsVoList.add(visitorsVo);
        }

        return visitorsVoList;
    }

    public List<QuanZiVo> createQuanZi(List<Publish> publishes, Map<Long, UserInfo> userInfoByUserId) {
        List<QuanZiVo> quanZiVoList = new ArrayList<>();
        for (Publish publish : publishes) {
            QuanZiVo quanZiVo = new QuanZiVo();
            // 圈子ID
            quanZiVo.setId(publish.getId().toHexString());
            // 圈子发布人ID
            quanZiVo.setUserId(publish.getUserId());
            // 圈子文本内容
            quanZiVo.setTextContent(publish.getText());
            // 圈子的图片
            quanZiVo.setImageContent(Convert.toStrArray(publish.getMedias()));
            // 圈子的发布时间
            quanZiVo.setCreateDate(RelativeDateFormat.format(new Date(publish.getCreated())));

            // 获取发布人的信息
            UserInfo userInfo = userInfoByUserId.get(publish.getUserId());
            // 头像
            quanZiVo.setAvatar(userInfo.getLogo());
            // 昵称
            quanZiVo.setNickname(userInfo.getNickName());
            // 性别
            quanZiVo.setGender(userInfo.getSex() == 1 ? "man" : "woman");
            // 年龄
            quanZiVo.setAge(userInfo.getAge());
            // 标签
            quanZiVo.setTags(StrUtil.splitToArray(userInfo.getTags(), ","));


            // 评论相关
            // 点赞数
            Long likeCount = Convert.toLong(redisTemplate.opsForHash().get(Constants.MOVEMENTS_INTERACT_KEY + publish.getId(), Constants.MOVEMENT_LIKE_HASHKEY));
            // 说明当前这个圈子没有人点赞 或者有人点赞但是Redis中没值
            if (ObjectUtil.isNull(likeCount)) {
                // 从MongoDB中进行查询
                likeCount = commentAPI.selectLikeSize(publish.getId().toHexString());
                // 把查询到数据给保存到Redis中
                redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + publish.getId(), Constants.MOVEMENT_LIKE_HASHKEY, likeCount.toString());
            }
            quanZiVo.setLikeCount(Convert.toInt(likeCount));

            // 评论数
            Long commentCount = Convert.toLong(redisTemplate.opsForHash().get(Constants.MOVEMENTS_INTERACT_KEY + publish.getId(), Constants.MOVEMENT_COMMENT_HASHKEY));
            if (ObjectUtil.isNull(commentCount)) {
                // 从MongoDB中进行查询
                commentCount = commentAPI.selectCommentSize(publish.getId().toHexString());
                // 把查询到数据给保存到Redis中
                redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + publish.getId(), Constants.MOVEMENT_COMMENT_HASHKEY, commentCount.toString());
            }
            quanZiVo.setCommentCount(Convert.toInt(commentCount));

            // 喜欢数
            Long loveCount = Convert.toLong(redisTemplate.opsForHash().get(Constants.MOVEMENTS_INTERACT_KEY + publish.getId(), Constants.MOVEMENT_LOVE_HASHKEY));
            if (ObjectUtil.isNull(loveCount)) {
                // 从MongoDB中进行查询
                loveCount = commentAPI.selectLoveSize(publish.getId().toHexString());
                // 把查询到数据给保存到Redis中
                redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + publish.getId(), Constants.MOVEMENT_LOVE_HASHKEY, loveCount.toString());
            }
            quanZiVo.setLoveCount(Convert.toInt(loveCount));

            // 是否点赞
            Boolean isLike = redisTemplate.opsForHash().hasKey(Constants.MOVEMENTS_INTERACT_KEY, Constants.MOVEMENT_ISLIKE_HASHKEY + UserThreadLocal.getUserId());
            //Boolean isLike = redisTemplate.opsForHash().hasKey(Constants.MOVEMENTS_INTERACT_KEY+quanZiVo.getId(), Constants.MOVEMENT_ISLIKE_HASHKEY + UserThreadLocal.getUserId());
            if (!isLike) {
                // 从MongoDB中进行查询
                isLike = commentAPI.selectIsLike(quanZiVo.getId(), UserThreadLocal.getUserId());
                if (isLike) {
                    // 把数据给保存到Redis中
                    redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + quanZiVo.getId(), Constants.MOVEMENT_ISLIKE_HASHKEY + UserThreadLocal.getUserId(), "1");
                }
            }
            quanZiVo.setHasLiked(isLike ? 1 : 0);

            // 是否喜欢
            Boolean isLove = redisTemplate.opsForHash().hasKey(Constants.MOVEMENTS_INTERACT_KEY, Constants.MOVEMENT_ISLOVE_HASHKEY + UserThreadLocal.getUserId());
            if (!isLove) {
                // 从MongoDB中进行查询
                isLove = commentAPI.selectIsLove(quanZiVo.getId(), UserThreadLocal.getUserId());
                if (isLove) {
                    // 把查询到数据给保存到Redis中
                    redisTemplate.opsForHash().put(Constants.MOVEMENTS_INTERACT_KEY + quanZiVo.getId(), Constants.MOVEMENT_ISLOVE_HASHKEY + UserThreadLocal.getUserId(), "1");
                }
            }

            quanZiVo.setHasLoved(isLove ? 1 : 0);

            // 写死距离
            quanZiVo.setDistance("1.2公里");

            quanZiVoList.add(quanZiVo);
        }
        return quanZiVoList;
    }

}
